#pragma once

#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <string>
#include <signal.h>
#include <pthread.h>
#include <functional>
#include "ThreadPool.hpp"
#include "Log.hpp"
#include "Comm.hpp"

using namespace lyrics;
using namespace ThreadPoolModule;
//匹配线程池的任务类型
using task_t = std::function<void()>;

const int gport = 8080;
#define BACKLOG 10

class TcpServer
{
private:
    struct ThreadData
    {
        int sockfd;
        TcpServer *server;
    };

public:
    TcpServer(const int port = gport) : _port(port), _isrunning(false)
    {
    }
    void InitTcpServer()
    {
        // 创建数据流套接字
        _listensockfd = socket(AF_INET, SOCK_STREAM, 0);
        // 创建失败
        if (_listensockfd < 0)
        {
            LOG(LogLevel::FATAL) << "socket error" << strerror(_listensockfd);
            Die(SOCK_ERR);
        }
        LOG(LogLevel::INFO) << "create socket success" << _listensockfd;
        // 创建成功
        struct sockaddr_in server;
        socklen_t len = sizeof(server);
        server.sin_family = AF_INET;
        server.sin_port = htons(_port);
        server.sin_addr.s_addr = INADDR_ANY;
        int n = ::bind(_listensockfd, CONV(&server), len);
        if (n < 0)
        {
            LOG(LogLevel::FATAL) << "bind error" << strerror(n);
            Die(BIND_ERR);
        }
        // bind成功
        LOG(LogLevel::INFO) << "bind success : " << _port;

        // 对客户端进行监听
        n = ::listen(_listensockfd, BACKLOG);
        if (n < 0)
        {
            LOG(LogLevel::FATAL) << "listen error" << strerror(n);
            Die(LISTEN_ERR);
        }
        // 自动回收子进程
        signal(SIGCHLD, SIG_IGN);
    }
    // 有this指针的hanlder方法
    void HanlderTest(int sockfd)
    {
        char inbuffer[1024];
        while (true)
        {
            int n = read(sockfd, inbuffer, sizeof(inbuffer) - 1);
            if (n > 0)
            {
                // 读取成功
                inbuffer[n] = 0;
                std::string echostr;
                LOG(LogLevel::INFO) << "receive data from client: " << inbuffer;
                echostr = "echo#: " + std::string(inbuffer);
                int m = write(sockfd, inbuffer, sizeof(inbuffer));
                // 读取失败
                if (m < 0)
                {
                    LOG(LogLevel::WARNING) << "write error" << strerror(m);
                    break;
                }
            }
            else if (n == 0)
            {
                LOG(LogLevel::INFO) << "client close the connection,the socket is " << sockfd;
                break;
            }
            else
                break;
        }
        close(sockfd);
    }
    static void *ThreadFunc(void *arg)
    {
        ThreadData *data = (ThreadData *)arg;
        data->server->HanlderTest(data->sockfd);
        return nullptr;
    }
    void StartTcpServer()
    {
        // 设置运行状态
        _isrunning = true;
        // 启动操作系统
        while (_isrunning)
        {
            // 远端客户端的sockaddr_in结构体
            struct sockaddr_in peer;
            // 结构体大小
            socklen_t len = sizeof(peer);
            LOG(LogLevel::INFO) << "waiting for client...";
            // 等待客户端连接
            int sockfd = ::accept(_listensockfd, CONV(&peer), &len);
            if (sockfd < 0)
            {
                LOG(LogLevel::WARNING) << "accept error" << strerror(sockfd);
                // 接受到一个新的连接，出错之后继续接收下一个，只需要包报一次错即可
                continue;
            }
            LOG(LogLevel::INFO) << "client connect success,the socket is " << sockfd;
            // 客户端连接成功

            // version-0 只允许一个客户端访问
            // 接收客户端传输的数据包
            // HanlderTest(sockfd);

            // version 1 允许多个客户端访问
            // 创建子进程
            //  pid_t id = fork();
            //  if(id == 0)
            //  {
            //      //子进程---关闭监听套接字
            //      ::close(_listensockfd);
            //      //这里创建子进程的子进程，让子进程直接退出，让孙子进程继续跑
            //      if(fork() > 0) exit(0);
            //      //子进程退出，所以孙子进程变成孤儿进程，所以会被操作系统回收
            //      HanlderTest(sockfd);
            //      //孙子进程跑完直接让孙子进程退出
            //      exit(0);
            //  }
            //  //因为父进程和子进程有两个独立的文件描述符，所以子进程和父进程分别关掉不需要的文件描述符
            //  ::close(sockfd);

            // version 2 多线程访问服务器
            //  pthread_t tid;
            //  //这里必须重新创建一个传递的参数，因为新线程没有执行的时候，参数可能被销毁
            //  ThreadData* data = new ThreadData();
            //  data->sockfd = sockfd;
            //  data->server = this;
            //  //这里不能直接传递Hanlder方法，因为hanlder方法中第一个参数有this指针，线程访问的参数中只有void*的函数
            //  pthread_create(&tid,nullptr,ThreadFunc,data);

            //version 3 线程池版本
            //绑定任务类型
            task_t  task = std::bind(&TcpServer::HanlderTest,this,sockfd);
            //任务插入线程池
            ThreadPool<task_t>::getInstance()->Equeue(task);  //插入任务
        }
        _isrunning = false;
    }
    ~TcpServer()
    {
    }

private:
    int _listensockfd;
    int _port;
    bool _isrunning;
};